/**
 * 
 */
package com.kjcxlab.avcviewer.audio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;

/**
 * @author efhkklm
 * 
 */
public class AudioDecoder implements Runnable {

	private MediaCodec mDecoder = null;
	//FIXME: Audio format hardcode here
	private MediaFormat mFormat = MediaFormat.createAudioFormat(
			"audio/mp4a-latm", 44100, 2);
	private AudioTrack mAudioTrack = null;
	private ByteBuffer[] inputBuffers = null;
	private ByteBuffer[] outputBuffers = null;
	private ArrayBlockingQueue<byte[]> outQueue = null;
	private int minBufferSize = 0;
	private final static String TAG = "Decoder";
	private byte[] inBuf = null;

	public AudioDecoder(ArrayBlockingQueue<byte[]> queue) {
		this.outQueue = queue;
	}

	public void init() {

		byte[] bytes = new byte[] { (byte) 19, (byte) -120 };// 0x12 0x12
		ByteBuffer bb = ByteBuffer.wrap(bytes);

		mFormat.setByteBuffer("csd-0", bb);

		String mime = mFormat.getString(MediaFormat.KEY_MIME);
		if (!mime.startsWith("audio/")) {
			Log.e(TAG, "Wrong format!");
			return;
		}
		/*
		 * Create a decoder base known media type
		 */
		try {
			mDecoder = MediaCodec.createDecoderByType(mime);
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (mDecoder == null) {
			Log.e(TAG, "Creat decoder error!");
			return;
		}
		mDecoder.configure(mFormat, null, null, 0);
		mDecoder.start();
		inputBuffers = mDecoder.getInputBuffers();
		outputBuffers = mDecoder.getOutputBuffers();

		/*
		 * Create a AudioTrack and set parameters
		 */
		int channelConfiguration = mFormat
				.getInteger(MediaFormat.KEY_CHANNEL_COUNT) == 1 ? AudioFormat.CHANNEL_OUT_MONO
				: AudioFormat.CHANNEL_OUT_STEREO;
		minBufferSize = AudioTrack.getMinBufferSize(
				mFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE),
				channelConfiguration, AudioFormat.ENCODING_PCM_16BIT);
		mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
				mFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE),
				channelConfiguration, AudioFormat.ENCODING_PCM_16BIT,
				minBufferSize * 2, AudioTrack.MODE_STREAM);
		//mAudioTrack.setVolume(1);
		mAudioTrack.play();
	}

	/* decode one h264 frame */
	private void decodeOneFrame() {

		if (inBuf != null) {
			int inputBufferIndex = mDecoder.dequeueInputBuffer(100);
			if (inputBufferIndex < 0) {
				Log.e(TAG, "No available audio codec input buffer");
				return;
			}
			ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
			inputBuffer.clear();
			if (inputBuffer.capacity() >= inBuf.length) {
				inputBuffer.put(inBuf, 0, inBuf.length);
				mDecoder.queueInputBuffer(inputBufferIndex, 0, inBuf.length, 0,
						0);
			} else {
				System.out.println("Input fram too long!" + inBuf.length);
			}

			MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
			int res = -1;
			res = mDecoder.dequeueOutputBuffer(info, 100);
			if (res >= 0) {
				int outputBufferIndex = res;
				ByteBuffer buf = outputBuffers[outputBufferIndex];
				final byte[] chunk = new byte[info.size];
				buf.get(chunk);
				buf.clear();
				if (chunk.length > 0) {
					mAudioTrack.write(chunk, 0, chunk.length);
					//Log.d(Constants.AUDIO_TAG, "play a frame");
				}
				mDecoder.releaseOutputBuffer(outputBufferIndex, false);
			} else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
				outputBuffers = mDecoder.getOutputBuffers();
			} else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
				MediaFormat oformat = mDecoder.getOutputFormat();
				Log.w(TAG, "output format has changed to " + oformat);
			} else {
				Log.w(TAG, "dequeueOutputBuffer returned " + res);
			}
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			try {
				this.inBuf = outQueue.take();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			decodeOneFrame();
		}
	}
}
